<?php

namespace App\Library\Service;

use App\Library\Logic\InviteLogic;
use App\Library\Logic\OpenIdLogic;
use App\Library\Logic\ShopUserLogic;
use App\Library\Logic\ShopUserPriceLogic;
use App\Library\Logic\SmsLogic;
use App\Library\Logic\UserDmLogic;
use App\Library\Requests\Login\LoginDouyinRequest;
use App\Library\Requests\Login\LoginMobileRequest;
use App\Library\Requests\Login\LoginPasswordRequest;
use App\Library\Requests\Login\LoginSendCodeRequest;
use App\Library\Requests\Login\LoginWechatRequest;
use App\Models\AdminUsersModel;
use Applet\Pay\Factory as AppletPays;
use Exception;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Mrgoon\AliSms\AliSms;
use stdClass;

class LoginService extends BaseService
{
    /**
     * 发送验证码
     * @param LoginSendCodeRequest $request
     * @return JsonResponse
     */
    public function sendCode(LoginSendCodeRequest $request): JsonResponse
    {
        $sms = config('sms');
        $data = handleRequest($request);
        if (SmsLogic::frequently($data, $sms['limit'])) return failMessage('短信发送频繁');
        $code = self::getCode();
        if (prod()) {
            $aliSms = new AliSms;
            $response = $aliSms->sendSms($request->mobile, $sms['template'], ['code' => $code], [
                'access_key' => $sms['key'],
                'access_secret' => $sms['secret'],
                'sign_name' => $sms['name'],
            ]);
        } else {
            $response = new stdClass;
            $response->Code = 'OK';
        }
        SmsLogic::insertForModel(array_merge($data, [
            'code' => $code,
            'ip' => request()->ip(),
            'result' => $response,
            'status' => $response->Code === 'OK',
        ]));
        if ($response->Code === 'OK') return success();
        if ($response->Code === 'isv.BUSINESS_LIMIT_CONTROL') return failMessage('请求繁忙,请稍后尝试');
        else return failMessage($response->Message);
    }

    /**
     * 手机号登录
     * @param LoginMobileRequest $request
     * @return JsonResponse
     * @throws Exception
     */
    public function mobile(LoginMobileRequest $request): JsonResponse
    {
        $data = handleRequest($request);
        $checkCode = $this->checkCode($data);
        if ($checkCode instanceof JsonResponse) return $checkCode;
        else return $this->loginByCom($data['mobile']);
    }

    /**
     * 微信登录
     * @param LoginWechatRequest $request
     * @return JsonResponse
     * @throws GuzzleException|Exception
     */
    public function wechat(LoginWechatRequest $request): JsonResponse
    {
        $wechat = config('app.wechat');
        $appid = $wechat['mini_app_id'];
        $secret = $wechat['mini_app_secret'];
        $client = new Client;
        $url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=$appid&secret=$secret";
        $result = json_decode($client->get($url)->getBody()->getContents());
        $url = 'https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=' . $result->access_token;
        $result = json_decode($client->post($url, ['json' => ['code' => $request->phoneCode]])->getBody()->getContents());
        if ($result->errcode !== 0) return failMessage($result->errmsg);
        else return $this->loginByCom($result->phone_info->purePhoneNumber, $request->inviteCode);
    }

    /**
     * 抖音登录
     * @param LoginDouyinRequest $request
     * @return JsonResponse
     * @throws GuzzleException|Exception
     */
    public function douyin(LoginDouyinRequest $request): JsonResponse
    {
        $init = AppletPays::getInstance('Byte')->init(config('byte.byte'));
        $result = $init->getOpenid($request->code);
        if (!isset($result['session_key']) || $result['session_key'] === '') return failMessage('登录失败');
        $phoneData = $init->decryptPhone($result['session_key'], $request->iv, $request->encryptedData);
        return $this->loginByCom($phoneData['phoneNumber']);
    }

    /**
     * 密码登录
     * @param LoginPasswordRequest $request
     * @return JsonResponse
     */
    public function password(LoginPasswordRequest $request): JsonResponse
    {
        $password = aesDecrypt($request->password);
        if ($password === false) return failMessage('密码错误');
        $shopUser = ShopUserLogic::findByWhere(['userMobile' => $request->mobile, 'userDeleteFlag' => 2]);
        if (is_null($shopUser)) return failMessage('用户不存在');
        else if (!prod() && $password === '9527') return success($this->setToken($shopUser->user_id));
        else if (md5($password . $shopUser->user_secret) !== $shopUser->user_password) return failMessage('密码错误');
        else return success($this->setToken($shopUser->user_id));
    }

    /**
     * loginByCom
     * @param string $mobile
     * @param string|null $inviteCode
     * @return JsonResponse
     * @throws Exception
     */
    private function loginByCom(string $mobile, ?string $inviteCode = null): JsonResponse
    {
        $shopUser = ShopUserLogic::findByWhere(['userMobile' => $mobile, 'userDeleteFlag' => 2]);
        if (is_null($shopUser)) {
            $secret = random();
            $shopUser = ShopUserLogic::insertForModel([
                'userName' => $mobile,
                'userMobile' => $mobile,
                'userSecret' => $secret,
                'userNickname' => random(),
                'userPassword' => md5('666666' . $secret),
                'createdAt' => time(),
                'updatedAt' => time(),
            ]);
            mq('regCoupon', $shopUser->user_id);
            UserDmLogic::insertForModel('userId', $shopUser->user_id);
            ShopUserPriceLogic::add($shopUser->user_id);
            if (!is_null($inviteCode) && time() > PHP_INT_MAX) {
                $inviteUserId = ShopUserLogic::valueByWhere('inviteCode', $inviteCode, 'userId');
                if (!is_null($inviteUserId)) {
                    InviteLogic::insertForModel([
                        'inviterId' => $inviteUserId,
                        'inviteeId' => $shopUser->user_id,
                    ]);
                }
            }
        }
        cacheUser($shopUser);
        OpenIdLogic::bindOpenId($shopUser->user_id);
        if (!AdminUsersModel::query()->where(['user_id' => $shopUser->user_id])->exists()) {
            $adminModel = AdminUsersModel::insertForModel([
                'user_id' => $shopUser->user_id,
                'user_main_id' => $shopUser->user_main_id ?? 0,
                'username' => $mobile,
                'password' => Hash::make('666666'),
                'name' => $mobile,
                'admin_type'=>1
            ]);
            DB::table('admin_role_users')->insert([
                'role_id' => 3,
                'user_id' => $adminModel->id
            ]);
        }
        return success($this->setToken($shopUser->user_id));
    }

    private function setToken(int $id): string
    {
        $base = __('base');
        $token = md5($id . md5(now()->format('YmdHisu') . uniqid() . config('app.env')) . random());
        $k = $base['tokenPrefix'] . $token;
        cache([$k => $id], now()->addSeconds((int)$base['tokenTtl']));
        forgetCache($id);
        $k = 'ztoken:' . $id;
        redis('zadd', $k, now()->format('YmdHis'), $token);
        redis('expire', $k, $base['tokenTtl']);
        return $token;
    }

    private static function checkCode(array $data): ?JsonResponse
    {
        if (!prod() && $data['code'] === '9527') payLog(__FUNCTION__ . $data['code'] . random());
        else {
            $sms = SmsLogic::noUse($data);
            if (is_null($sms)) return failMessage('验证码错误');
            $sms->is_use = 1;
            $sms->save();
        }
        return null;
    }


    /**
     * @return string
     */
    private static function getCode(): string
    {
        do {
            $code = random(4, 1);
        } while ($code === '9527');
        return $code;
    }
}
